package tool
{
	import away3d.arcane;
	use namespace arcane; 
	
	import assets.Asset;
	
	import away3d.animators.SkeletonAnimationSet;
	import away3d.animators.data.JointPose;
	import away3d.animators.data.Skeleton;
	import away3d.animators.data.SkeletonJoint;
	import away3d.animators.data.SkeletonPose;
	import away3d.animators.nodes.SkeletonClipNode;
	import away3d.core.base.SkinnedSubGeometry;
	import away3d.core.base.SubGeometry;
	import away3d.core.math.Quaternion;
	import away3d.lights.DirectionalLight;
	import away3d.lights.LightBase;
	import away3d.lights.PointLight;
	import away3d.lights.shadowmaps.NearDirectionalShadowMapper;
	import away3d.materials.methods.NearShadowMapMethod;
	import away3d.materials.methods.SoftShadowMapMethod;
	
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	import flash.system.System;
	
	import views.EnvLight;
	import game.mission.MissionDetail;
	
	
	/**
	 * xml数据都在这里解析
	 */
	public class DataParser
	{
		
		/**
		 * 解析任务信息
		 */		
		public static function getMissionInfo(xml:XML):Object
		{
			var id:uint = xml.@id;
			var name:String	= xml.@name;	
			var type:int = xml.@type;			
			var loop:Boolean = xml.@loop=="true"?true:false;		
			var describe:String = xml.describe;
			
			var tipsType:uint	= xml.@tipsType;
			var tipsText:String = xml.@tipsText;
			
			var startTime:uint = xml.@startTime;
			var valid:int = xml.@valid;
			
			//--------
			var tipsPos:Object =	{
				
				offsetX:Number(xml.tipsPos.@offsetX),
				
				offsetY:Number(xml.tipsPos.@offsetX),
				
				ui:int(xml.tipsPos.@ui)
				
				};
			
			//--------acquireRequire
			var acquireRequire:Object = {
				
				level:int(xml.acquireRequire.@level),
				
				money:int(xml.acquireRequire.@money)
			}
			
			var ary:Array = new Array;
			for each(var data:XML in xml.acquireRequire.item)
			{
				ary.push({
					
						itemID:int(data.@id),
						
						inc:int(data.@inc),
						
						itemNum:int(data.@num)
					})
			}
			
			acquireRequire.item = ary;
			
			//--------completeRequire
			var completeRequire:Object = {
				
				ui:int(xml.completeRequire.@ui),			
				
				map:int(xml.completeRequire.@map)
			}
			
			ary = new Array;
			for each(data in xml.completeRequire.item)
			{
				ary.push({
					
					id:int(data.@id),
					
					inc:int(data.@inc),
					
					num:int(data.@num)
				})
			}
			completeRequire.item = ary;
			
			ary = new Array;
			for each(data in xml.completeRequire.enemy)
			{
				ary.push({
					
					id:int(data.@id),
					
					num:int(data.@num)
				})
			}
			
			completeRequire.enemy = ary;
			
			//--------reward
			var reward:Object={
				
				money:int(xml.reward.@money),
				
				exp:int(xml.reward.@exp)
			}
				
			ary = new Array;
			for each(data in xml.reward.mission)
			{
				ary.push({
					
					id:int(data.@id),
					
					type:int(data.@type)
				})
			}
			
			reward.mission = ary;
			
			ary = new Array;
			for each(data in xml.reward.item)
			{
				ary.push({
					
					id:int(data.@id),
					
					inc:int(data.@inc),
					
					num:int(data.@num)
				})
			}
			
			reward.item = ary;
			
			//-----------------------------
			
			var missionData:Object = {};
			
			missionData.id = id;
			missionData.name = name;
			missionData.type = type;
			missionData.loop = loop;
			missionData.describe = describe;
			missionData.tipsType = tipsType;
			missionData.tipsText = tipsText;
			missionData.tipsPos = tipsPos;
			missionData.startTime = startTime;
			missionData.valid = valid;
			missionData.acquireRequire = acquireRequire;
			missionData.completeRequire = completeRequire;
			missionData.reward = reward;
			
			return missionData;
		}
		
		
		/**
		 * 解析灯光与全局阴影
		 */		
		public static function getLight(xml:XML):Object
		{
			var data:Object = new Object;
			
			var lights:Array = new Array;
			var shadow:NearShadowMapMethod=null;
			
			for each(var str:String in xml.directLights.light)
			{
				var dLight:DirectionalLight = new DirectionalLight;
				setLightValues(dLight,str);
				
				dLight.lookAt(new Vector3D);
				
				lights.push(dLight);
			}
			
			for each(var l:DirectionalLight in lights)
			{
				if(l.castsShadows)
				{
					var shadowMapper:NearDirectionalShadowMapper = new NearDirectionalShadowMapper(0.03);
					l.shadowMapper = shadowMapper;
					var sm:SoftShadowMapMethod = new SoftShadowMapMethod(l,5);
					shadow = new NearShadowMapMethod(sm,.1);
					shadow.epsilon = 0.0003;
					break;
				}
			}
			
			for each(str in xml.pointLights.light)
			{
				var omni:PointLight = new PointLight;
				setLightValues(omni,str);
				
				lights.push(omni);
			}
			
			for each(var val:XML in xml.probeLights.light)
			{
				var pLight:EnvLight = new EnvLight(val.@map);
				lights.push(pLight);
			}
			
			data.lights = lights;
			data.shadow = shadow;
			
			return data;
		}
		
		/**
		 * 解析静态3D模型
		 */		
		public static function getStaticMeshFromData(data:XML):Object
		{
			var obj:Object;
			var geometrys:Array = new Array;
			var castShadow:Boolean = false;
			
			for each (var xml:XML in data.children())
			{
				var fixalpha:String = xml.fixTranslucent;
				
				var shadow:String = xml.@castShadow;
				if(shadow == "true")castShadow = true;
				
				var subGeo:SubGeometry = new SubGeometry;
				buildStaticShape(subGeo,xml);
				
				geometrys.push(
					{
						fixalpha:fixalpha,
						subGeo:subGeo,
						mat:new XML(xml.material)
					}
				);
			}
			
			obj = 
				{
					castShadow:castShadow,
					geometrys:geometrys
				}
			
			return obj;
		}
		
		/**
		 * 解析包含蒙皮信息与骨骼动画的3D模型
		 */		
		public static function getSkinedMeshFromData(meshData:XML,skeletonData:XML):Object
		{
			var geometrys:Array = new Array;
			var castShadow:Boolean = false;
			
			for each (var xml:XML in meshData.children())
			{
				var fixalpha:String = xml.fixTranslucent;
				var shadow:String = xml.@castShadow;
				if(shadow == "true")castShadow = true;
				
				var jointsPerVetex:uint = uint(xml.@jointsPerVetex)
				
				var subGeo:SkinnedSubGeometry = new SkinnedSubGeometry(jointsPerVetex);
				buildSkinGeo(subGeo,xml);
				
				geometrys.push(
					{
						fixalpha:fixalpha,
						subGeo:subGeo,
						mat:new XML(xml.material)
					}
				);
			}
			
			var skeletonURL:String = Global.assetPath+meshData.@skeleton;
			
			var skeleton:Object = null;
			if(skeletonData) skeleton= getAnimateSkeleton(skeletonData);
			
			var obj:Object = {
				castShadow:castShadow,
				skeletonURL:skeletonURL,
				skeleton:skeleton,
				geometrys:geometrys
			}
			
			return obj
		}
		
		/**
		 * 解析包含动画的骨骼数据
		 */		
		public static function getAnimateSkeleton(xml:XML):Object
		{
			var jointsPerVetex:uint = uint(xml.@jointsPerVetex);
			//var withScale:Boolean = Boolean(xml.@withScale);
			//trace(withScale);
			
			var _skeleton:Skeleton;
			var _animationSet:SkeletonAnimationSet = new SkeletonAnimationSet(jointsPerVetex);
			
			var boneDatas:Array = new Array;
			for each (var val:String in xml.bones.bone)
			{
				var strs:Array = val.split(",");
				var name:String = strs[0];
				var parentIndex:int = int(strs[1])
				var pos:Vector3D = new Vector3D(Number(strs[2]),Number(strs[3]),Number(strs[4]));
				var rot:Quaternion = new Quaternion(Number(strs[5]),Number(strs[6]),Number(strs[7]),Number(strs[8]));
				//var sca:Vector3D;
				//if(withScale)sca = new Vector3D(Number(strs[9]),Number(strs[10]),Number(strs[11]));
				
				boneDatas.push(
					{
						name: name,
						parentIndex: parentIndex,
						pos: pos,
						rot: rot
						//sca:sca
					}
				);
			}
			
			_skeleton = buildSkeletonJoints(boneDatas); 
			
			for each (var animate:XML in xml.animations.animation)
			{
				name = animate.@name;
				var step:Number = Number(animate.@step);
				
				var animationClip:SkeletonClipNode = new SkeletonClipNode();
				animationClip.name = name;
				
				var jointStr:String;
				var jointValstr:Array;
				var jointpose:JointPose;
				var skeletonpose:SkeletonPose;
				for each(var frame:String in animate.frame)
				{
					skeletonpose = new SkeletonPose;
					for each(jointStr in frame.split(";"))
					{
						var jointVals:Vector.<Number> = new Vector.<Number>;
						
						jointValstr = jointStr.split(",");
						for each(var str:String in jointValstr)jointVals.push(Number(str));
						
						jointpose = new JointPose;
						jointpose.translation = new Vector3D(jointVals[0],jointVals[1],jointVals[2]);
						jointpose.orientation = new Quaternion(jointVals[3],jointVals[4],jointVals[5],jointVals[6]);
						//JointPose没有scale
						
						skeletonpose.jointPoses.push(jointpose);
					}
					
					animationClip.addFrame(skeletonpose,1000*step/Global.maxframeRate);
				}
				_animationSet.addAnimation(animationClip);
				
				System.disposeXML(animate);
			}
			
			return {
				skeleton:_skeleton,
				animationSet:_animationSet
			}
		}
		
		/////////////////////////////////////////////////////////////////////////////////
		private static function setLightValues(light:LightBase,str:String):void
		{
			var valStrArray:Array = str.split(",");
			
			var posX:Number = Number(valStrArray[0]);
			var posY:Number = Number(valStrArray[1]);
			var posZ:Number = Number(valStrArray[2]);
			
			var color:uint = uint(valStrArray[3]);
			var ambColor:uint = uint(valStrArray[4]);
			var intensity:Number = Number(valStrArray[5]);
			
			light.x = posX;
			light.y = posY;
			light.z = posZ;
			
			light.color = color;
			light.ambientColor = ambColor;
			light.ambient = 1;
			light.diffuse = intensity;
			light.specular = valStrArray[6]=="1"?1:0;
			light.castsShadows = valStrArray[7]=="1"?true:false;
		}
		///////////////////
		private static function buildStaticShape(subGeo:SubGeometry,xml:XML):void
		{
			var verts:Vector.<Number> = numberDataFromString(xml.vertexs);
			var faces:Vector.<uint> = uintDataFromString(xml.faces);
			var UVs:Vector.<Number> = numberDataFromString(xml.UVs);
			var normals:Vector.<Number> = numberDataFromString(xml.normals);
			
			//-----------------
			subGeo.updateVertexData(verts);
			subGeo.updateIndexData(faces);
			subGeo.updateUVData(UVs);
			subGeo.updateVertexNormalData(normals);
			
			var secUVstr:String = xml.secUVs;
			if(secUVstr!="")
			{
				var secUVs:Vector.<Number> = numberDataFromString(secUVstr);
				subGeo.updateSecondaryUVData(secUVs);
			}
		}
		
		private static  function buildSkinGeo(skinSubGeo:SkinnedSubGeometry,xml:XML):void
		{
			var verts:Vector.<Number> = numberDataFromString(xml.vertexs);
			var UVs:Vector.<Number> = numberDataFromString(xml.UVs);
			var normals:Vector.<Number> = numberDataFromString(xml.normals);
			
			var secUVs:Vector.<Number> = null;
			var secUVstr:String = xml.secUVs;
			if(secUVstr!="")secUVs = numberDataFromString(secUVstr);
			
			var data:Vector.<Number> = mixSkinnedGeoData(verts,normals,UVs,secUVs);
			
			var faces:Vector.<uint> = uintDataFromString(xml.faces)
			var jointIndexs:Vector.<Number> = numberDataFromString(xml.jointIndices,3);
			var jointWeights:Vector.<Number> =  numberDataFromString(xml.jointWeights);
			//-----------------
			skinSubGeo.updateData(data);
			skinSubGeo.updateIndexData(faces);
			skinSubGeo.updateJointIndexData(jointIndexs);
			skinSubGeo.updateJointWeightsData(jointWeights);
			//skinSubGeo.autoDeriveVertexTangents = true;
		}
		
		private static function numberDataFromString(str:String,val:Number=1):Vector.<Number>
		{
			var vtStrArray:Array = str.split(",");
			var datas:Vector.<Number> = new Vector.<Number>;
			for each(var v:String in vtStrArray)datas.push(val * Number(v));
			
			return datas;
		}
		
		private static function uintDataFromString(str:String):Vector.<uint>
		{
			var vtStrArray:Array = str.split(",");
			var datas:Vector.<uint> = new Vector.<uint>;
			for each(var v:String in vtStrArray)datas.push(uint(v));
			
			return datas;
		}
		
		private static function mixSkinnedGeoData(posData:Vector.<Number>,norData:Vector.<Number>,uvData:Vector.<Number>,secUvData:Vector.<Number>):Vector.<Number>
		{
			var data:Vector.<Number> = new Vector.<Number>;
			var len:uint = posData.length/3;
			
			for(var i:uint=0;i<len;i++)
			{
				var posX:Number = posData[i*3];
				var posY:Number = posData[i*3+1];
				var posZ:Number = posData[i*3+2];
				
				var norX:Number = norData[i*3];
				var norY:Number = norData[i*3+1];
				var norZ:Number = norData[i*3+2];
				
				var u:Number = uvData[i*2];
				var v:Number = uvData[i*2+1];
				
				var u2:Number = 0;
				var v2:Number = 0;
				if(secUvData)
				{
					u2 = secUvData[i*2];
					v2 = secUvData[i*2+1];
				}
				
				data.push(posX,posY,posZ,norX,norY,norZ,0,0,0,u,v,u2,v2);
			}
			
			return data;
		}
		
		/**
		 * away3D的骨骼现在还不支持缩放动画
		 */		
		private static function buildSkeletonJoints(BoneDatas:Array):Skeleton
		{
			var skeleton:Skeleton = new Skeleton();
			var joint:SkeletonJoint;
			for(var i:uint = 0;i<BoneDatas.length;i++)
			{
				var bnData:Object = BoneDatas[i];
				var pos:Vector3D = bnData.pos;
				var quat:Quaternion = bnData.rot;
				//var sca:Vector3D = bnData.sca;
				
				var bindPose:Matrix3D = quat.toMatrix3D();
				bindPose.appendTranslation(pos.x, pos.y, pos.z);
				//if(sca)bindPose.appendScale(sca.x,sca.y,sca.z);
				var inv:Matrix3D = bindPose.clone();
				inv.invert();
				
				joint = new SkeletonJoint();
				joint.name = bnData.name;
				
				//				所有骨骼设置为第一根骨骼的子物体
				if(i == 0)joint.parentIndex = -1;
				else joint.parentIndex = 0;
				
				//				按输出文件的父子关系设置				
				//				var parentIndex:int = bnData.parentIndex;
				//				joint.parentIndex = parentIndex;
				
				joint.inverseBindPose = inv.rawData;	
				
				skeleton.joints[i] = joint;
			}
			
			return skeleton;
		}
	}
}